package org.gwtrpcspring;


import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.google.gwt.user.server.rpc.RPCRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;

public class RemoteServiceDispatcher extends RemoteServiceServlet {
	/**
	 * 
	 */
	private static final long serialVersionUID = 7132243934574518263L;

	public RemoteServiceDispatcher() {
	}

	@Override
	public String processCall(String payload) throws SerializationException {
		try {
			RPCRequest rpcRequest = RPC.decodeRequest(payload, null,
					this);
			onAfterRequestDeserialized(rpcRequest);
			Object service = getService(rpcRequest.getMethod()
					.getDeclaringClass());
			
			return invokeAndEncodeResponse(service, rpcRequest.getMethod(),
					rpcRequest.getParameters(), rpcRequest
							.getSerializationPolicy());
		} catch (IncompatibleRemoteServiceException ex) {
			log(
					"An IncompatibleRemoteServiceException was thrown while processing this call.",
					ex);
			return RPC.encodeResponseForFailure(null, ex);
		}
	}
	
	  protected String invokeAndEncodeResponse(Object target,
		      Method serviceMethod, Object[] args,
		      SerializationPolicy serializationPolicy) throws SerializationException {

		RemoteServiceUtil.setThreadLocalRequest(getThreadLocalRequest());
		RemoteServiceUtil.setThreadLocalResponse(getThreadLocalResponse());
		RemoteServiceUtil.setThreadLocalContext(getServletContext());
	  
		String response = RPC.invokeAndEncodeResponse(target, serviceMethod,
				args, serializationPolicy);

		RemoteServiceUtil.setThreadLocalRequest(null);
		RemoteServiceUtil.setThreadLocalResponse(null);
		RemoteServiceUtil.setThreadLocalContext(null);
		
		return response;
		
	  }


	/**
	 * getService will look in context for bean with name of interface. It also
	 * verifies the bean implements the given interface.
	 * 
	 * @param serviceInterfaceName
	 * @return
	 * @throws BeansException
	 * @throws ClassNotFoundException
	 */
	protected Object getService(Class<?> type) throws BeansException {

		Object service = BeanFactoryUtils.beanOfTypeIncludingAncestors(
				WebApplicationContextUtils
						.getWebApplicationContext(getServletContext()), type);
		return service;
	}

}
